﻿using System;
using System.Collections.Generic;
 
 
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Globalization;
using System.Drawing.Drawing2D;
using System.Security.Cryptography;

namespace Kaiser.Utilities.Captcha
{
    public class CaptchaImage 
    {
        private readonly Random r;
        private string captchaValue;
        private Point fgAlpha;

        public CaptchaImage()
        {
            r = GenerateRandomInit();
            EllipseCount = 50;
            IsColoured = true;
            BgColorRange = new Point(180, 256);
            FgColorRange = new Point(0, 150);
            NoiseColorRange = new Point(100, 256);
            BgColor = Color.White;
            XShift = new Point(-10, 10);
            YShift = new Point(-8, 8);
            XDistortion = new Point(5, 10);
            YDistortion = new Point(0, 7);
            FgAlpha = new Point(50, 70);
            Angle = new Point(-45, 45);
            NoiseCount = 1000;
        }

        public int EllipseCount { get; set; }
        public bool IsColoured { get; set; }
        public Point BgColorRange { get; set; }
        public Point FgColorRange { get; set; }
        public Point NoiseColorRange { get; set; }
        public Color BgColor { get; set; }
        public Point XShift { get; set; }
        public Point YShift { get; set; }
        public Point XDistortion { get; set; }
        public Point YDistortion { get; set; }
        public Point Angle { get; set; }
        public int NoiseCount { get; set; }

        public Point FgAlpha
        {
            get { return fgAlpha; }
            set
            {
                if (value.X < 0 || value.X > 100 || value.Y < 0 || value.Y > 100)
                    throw new ArgumentException("FgAlpha between 0 and 100");
                fgAlpha = new Point(value.X, value.Y);
            }
        }

        public string CaptchaValue
        {
            get
            {
                captchaValue = GenerateNewCaptchaValue(4);
                return captchaValue;
            }
        }

        #region ICaptchaImage Members

        public string CaptchaUniqueId { get; set; }

        public void SaveImageToStream(Stream outputStream, int quality, int width, int height)
        {
            SaveImageToStream(outputStream, quality, width, height, captchaValue);
        }

        public void SaveImageToStream(Stream outputStream, int quality, int width, int height, string s)
        {
            if (quality < 0 || quality > 100) throw new ArgumentException("quality must be between 0 and 100");
            ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
            Encoder myEncoder = Encoder.Quality;
            var myEncoderParameters = new EncoderParameters(1);
            var myEncoderParameter = new EncoderParameter(myEncoder, quality);
            myEncoderParameters.Param[0] = myEncoderParameter;
            Bitmap b = CreateCaptchaImage(width, height, s);
            b.Save(outputStream, myImageCodecInfo, myEncoderParameters);
            b.Dispose();
        }

        #endregion

        #region ICaptchaValue Members

        public string RenderedValue
        {
            get { return string.IsNullOrWhiteSpace(captchaValue) ? CaptchaValue : captchaValue; }
        }

        public Bitmap CaptchaBitmap
        {
            get
            {
                Bitmap b = CreateCaptchaImage(110, 45, CaptchaValue);
                return b;

            }
        }

        #endregion

        private string GenerateNewCaptchaValue()
        {
            var digits = new string(Enumerable.Repeat(Enumerable.Range(0, 10), 4).SelectMany(v => v)
                                        .OrderBy(v => Guid.NewGuid()).Take(4).Select(
                                            v => v.ToString(CultureInfo.InvariantCulture)[0]).ToArray());
            return digits;
        }
        private string GenerateNewCaptchaValue(int num)
        {
            string str = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            char[] chastr = str.ToCharArray();
            string code = "";
            Random rd = new Random();
            int i;
            for (i = 0; i < num; i++)
            {
                code += str.Substring(rd.Next(0, str.Length), 1);
            }
            return code;
        }

        private Bitmap CreateCaptchaImage(int width, int height, string s)
        {
            var image = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(image);
            try
            {
                //生成随机生成器
                var random = new Random();

                //清空图片背景色
                g.Clear(Color.White);

                // 画图片的背景噪音线
                int i;
                for (i = 0; i < 25; i++)
                {
                    int x1 = random.Next(image.Width);
                    int x2 = random.Next(image.Width);
                    int y1 = random.Next(image.Height);
                    int y2 = random.Next(image.Height);
                    g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
                }

                var font = new Font("Arial", 28, (FontStyle.Bold));
                var brush =
                    new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height),
                                            Color.Blue, Color.DarkRed, 1.2F, true);
                g.DrawString(s, font, brush, 2, 2);

                //画图片的前景噪音点
                g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
                var ms = new MemoryStream();
                image.Save(ms, ImageFormat.Gif);
                g.Dispose();
            }
            catch
            {
                g.Dispose();
                image.Dispose();
            }
            return image;

            //var b = new Bitmap(width, height);
            //Graphics g = Graphics.FromImage(b);
            //CreateBackground(g);
            //PrintChars(g, s);
            //CreateNoise(g);
            //g.Dispose();
            //return b;
        }

        private void CreateBackground(Graphics g)
        {
            Size imgSize = g.VisibleClipBounds.Size.ToSize();
            g.FillRectangle(new SolidBrush(BgColor), g.VisibleClipBounds);
            for (int i = 0; i < EllipseCount; i++)
                g.FillEllipse(
                    new SolidBrush(GetRandomColor(BgColorRange)),
                    r.Next(-imgSize.Width / 4, imgSize.Width), r.Next(-imgSize.Height / 4, imgSize.Height),
                    r.Next(imgSize.Width / 4, imgSize.Width / 2), r.Next(imgSize.Height / 4, imgSize.Height / 2)
                    );
        }

        private void PrintChars(Graphics g, string s)
        {
            if (string.IsNullOrEmpty(s)) return;

            Size imgSize = g.VisibleClipBounds.Size.ToSize();
            int lW = imgSize.Width / s.Length;
            int lH = imgSize.Height;
            for (int i = 0; i < s.Length; i++)
            {
                float[][] colorMatrixElements = {
                                                    new float[] {1, 0, 0, 0, 0},
                                                    new float[] {0, 1, 0, 0, 0},
                                                    new float[] {0, 0, 1, 0, 0},
                                                    new[] {0, 0, 0, GetRandom(FgAlpha)/100f, 0},
                                                    new float[] {0, 0, 0, 0, 1}
                                                };
                var colorMatrix = new ColorMatrix(colorMatrixElements);
                var imageAttr = new ImageAttributes();
                imageAttr.SetColorMatrix(colorMatrix);

                Bitmap b = PrintChar(s.Substring(i, 1));
                int x = GetRandom(XShift, i == 0);
                int y = GetRandom(YShift, i == 0);

                var p = new[] {
                                  new Point(lW*i + x + GetRandom(XDistortion, i == 0),
                                            y + GetRandom(YDistortion, i == 0)),
                                  new Point(lW*(i + 1) + x + GetRandom(XDistortion, i == 0),
                                            y + GetRandom(YDistortion, i == 0)),
                                  new Point(lW*i + x + GetRandom(XDistortion, i == 0),
                                            b.Height + y + GetRandom(YDistortion, i == 0))
                              };
                g.DrawImage(b, p, new Rectangle(0, 0, b.Width, b.Height), GraphicsUnit.Pixel, imageAttr);
                b.Dispose();
            }
        }

        private Bitmap PrintChar(string s)
        {
            var b = new Bitmap(56, 56);
            Graphics g = Graphics.FromImage(b);
            var f = new Font(new Font("Times", 30), FontStyle.Bold);
            Brush br = new SolidBrush(GetRandomColor(FgColorRange));
            g.TranslateTransform(-20, -20, MatrixOrder.Append);
            g.RotateTransform(GetRandom(Angle), MatrixOrder.Append);
            g.TranslateTransform(20, 20, MatrixOrder.Append);
            g.DrawString(s, f, br, new PointF(0, 0));
            return b;
        }

        private void CreateNoise(Graphics g)
        {
            Size imgSize = g.VisibleClipBounds.Size.ToSize();
            for (int i = 0; i < NoiseCount; i++)
            {
                var p = new Pen(GetRandomColor(NoiseColorRange));
                g.DrawEllipse(p, new Rectangle(r.Next(imgSize.Width), r.Next(imgSize.Height), 1, 1));
            }
        }

        private int GetRandom(Point range)
        {
            return r.Next(range.X, range.Y);
        }

        private int GetRandom(Point range, bool fromZero)
        {
            return r.Next(fromZero ? 0 : range.X, range.Y);
        }

        private Color GetRandomColor(Point range)
        {
            if (IsColoured)
                return Color.FromArgb(GetRandom(range), GetRandom(range), GetRandom(range));
            int c = GetRandom(range);
            return Color.FromArgb(c, c, c);
        }

        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        private static Random GenerateRandomInit()
        {
            var b = new byte[4];
            RandomNumberGenerator.Create().GetBytes(b);
            return new Random(b[0] + (b[1] << 8) + (b[2] << 16) + ((b[3] << 24)));
        }

    }
}
